package com.teradata.olingo.controller;

import com.teradata.olingo.data.DataProvider;
import com.teradata.olingo.processor.TechnicalActionProcessor;
import com.teradata.olingo.processor.TechnicalBatchProcessor;
import com.teradata.olingo.processor.TechnicalEntityProcessor;
import com.teradata.olingo.processor.TechnicalPrimitiveComplexProcessor;
import com.teradata.olingo.provider.EdmTechProvider;
import com.teradata.olingo.provider.GenericEdmTechProvider;
import lombok.extern.slf4j.Slf4j;
import org.apache.olingo.commons.api.edm.EdmException;
import org.apache.olingo.commons.api.edmx.EdmxReference;
import org.apache.olingo.commons.api.edmx.EdmxReferenceInclude;
import org.apache.olingo.server.api.*;
import org.apache.olingo.server.api.debug.DefaultDebugSupport;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.util.MultiValueMap;
import org.springframework.util.StreamUtils;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;
import java.net.URI;
import java.nio.charset.Charset;
import java.util.Collections;
import java.util.UUID;
import java.util.concurrent.Callable;

import static com.teradata.olingo.constant.OdataConst.NAMESPACE;
import static com.teradata.olingo.utils.UrlInit.createODataRequest;

/**
 * http://localhost:8080/odata?$appId=1917&$apiName=ahhja&$apiVersion=1.0&$orderby=1,2,3&$top=1000&$select=DEAL_DATE,PHONE_NO,SHOULD_PAY&$filter=DEAL_DATE%20eq%20201801%20and%20PHONE_NO%20contains%20(%27139%27)
 */
@RestController
@RequestMapping("/chinabond.svc")
@Slf4j
public class OdataController {

    private static final long serialVersionUID = 1L;

    /**
     * <p>ETag for the service document and the metadata document</p>
     * <p>We use the same field for service-document and metadata-document ETags.
     * It must change whenever the corresponding document changes.
     * We don't know when someone changed the EDM in a way that changes one of these
     * documents, but we do know that the EDM is defined completely in code and that
     * therefore any change must be deployed, resulting in re-loading of this class,
     * giving this field a new and hopefully unique value.</p>
     */
    private static final String metadataETag = "W/\"" + UUID.randomUUID() + "\"";

    /**
     * The split.
     */
    private int split = 0;

    @RequestMapping(value = {"","*/**"})
    public Callable<ResponseEntity<String>> callable(HttpServletRequest req) {
        // Servlet3.0 非阻塞
        // 好处是避免web server的连接池被长期占用而引起线程被阻塞，导致性能问题，
        // 调用后生成一个非web的服务线程来处理，增加web服务器的吞吐量。
        return new Callable<ResponseEntity<String>>() {
            @Override
            public ResponseEntity<String> call() throws Exception {
                //Thread.sleep(3 * 1000L);
                return process(req);
                //return "小单 - " + System.currentTimeMillis() + " IP:" + req.getRemoteAddr();
            }
        };
    }

    /**
     * Process.
     *
     * @param req the req
     * @return the response entity
     */
    public ResponseEntity<String> process(HttpServletRequest req) {
            try {
                OData odata = OData.newInstance();
                EdmxReference reference = new EdmxReference(URI.create("../v4.0/cs02/vocabularies/Org.OData.Core.V1.xml"));
                //reference.addInclude(new EdmxReferenceInclude("Org.OData.Core.V1", "Core"));
                reference.addInclude(new EdmxReferenceInclude(NAMESPACE, "Core"));
                final ServiceMetadata serviceMetadata = odata.createServiceMetadata(
                        //new EdmTechProvider(),
                        new GenericEdmTechProvider(),
                        Collections.singletonList(reference),
                        new MetadataETagSupport(metadataETag));


                HttpSession session = req.getSession(true);
                DataProvider dataProvider = (DataProvider) session.getAttribute(DataProvider.class.getName());
                if (dataProvider == null) {
                    dataProvider = new DataProvider(odata, serviceMetadata.getEdm());
                    session.setAttribute(DataProvider.class.getName(), dataProvider);
                    log.info("Created new data provider.");
                }


                ODataHttpHandler handler = odata.createHandler(serviceMetadata);

                // Register processors.
                handler.register(new TechnicalEntityProcessor(dataProvider, serviceMetadata));
                handler.register(new TechnicalPrimitiveComplexProcessor(dataProvider, serviceMetadata));
                handler.register(new TechnicalActionProcessor(dataProvider, serviceMetadata));
                handler.register(new TechnicalBatchProcessor(dataProvider));
                // Register helpers.
                handler.register(new ETagSupport());
                handler.register(new DefaultDebugSupport());

                ODataRequest oDataRequest = createODataRequest(req, split);

                //String oDataReqStr = JSON.toJSONString(oDataRequest);

                //System.out.println("oDataReqStr:" + oDataReqStr);

                ODataResponse oDataResponse = handler.process(oDataRequest);


                String responseStr = StreamUtils.copyToString(
                        oDataResponse.getContent(), Charset.defaultCharset());
                MultiValueMap<String, String> headers = new HttpHeaders();

                for (String key : oDataResponse.getAllHeaders().keySet()) {
                    //System.out.println("responseHeader: key:" + key + " value:" + oDataResponse.getAllHeaders().get(key).toString());
                    String primValue = oDataResponse.getAllHeaders().get(key).toString();
                    String value = primValue.substring(1, primValue.length() - 1);
                    //System.out.println("value:" + value);
                    headers.add(key, value);
                }
                return new ResponseEntity<String>(responseStr, headers,
                        HttpStatus.valueOf(oDataResponse.getStatusCode()));

            } catch (Exception ex) {
            throw new EdmException("未知错误");
        }

    }
}
